home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
039a
/
d3d.zip
/
D3D.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-30
|
62KB
|
2,249 lines
/* D3D: Designing in Three Dimensions
After compilation, this module (D3D) is to be linked
together with the modules
GRPACK1
HLPFUN
TRAFO
The complier is Turbo C, Huge Memory Model
*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <alloc.h>
#include <conio.h>
#include <string.h>
#include <process.h>
#include "grpack.h"
#include <dos.h>
#define BIG 1e10
#define IBIG 30000
#define EPS 1e-6
#define MAXFACES 3000
#define LIN1 156
#define LIN2 168
#define LIN3 180
void
initrotate(double a1, double a2, double a3,
double v1, double v2, double v3,
double alpha),
rotate(double x, double y, double z,
double *px1, double *py1, double *pz1),
init_viewport(void),
hlpfun(float rho, float theta, float phi, float surflimit),
coeff(float rho, float theta, float phi),
ermes(char *s);
int abs(int x);
static void
aspect(void),
getstr(int x, int y, char *str),
mv(float x, float y, float z),
dw(float x, float y, float z),
screencoor(int mode, int i, int *pX, int *pY),
viewing(float x, float y, float z,
double *pxe,double *pye,double *pze),
display(void),
plotpoint(int i, int Bold),
grmes(int meslin, char *s),
rdfile(int normal),
wrfile(void),
cursorcontrol(char direc),
cursor(float x, float y, float z, int new),
show(char c),
faces(void),
eyepos(void),
plotsph(float rho, float theta, float phi, int down),
enquire(int vpos, char *str, float *px),
textcursor(int col, int line),
checkfaces(int i),
infopage(int i),
helpinfo(void),
myungetch(char ch),
line3(int P, int Q),
checkall(void),
clear(void),
zoom(int large),
reposition(void),
plotaxes(int checksize),
fresh(void),
list_faces(void),
points(void),
transform(void),
check_alloc(int i),
asksave(void),
entire(void),
open_plotfile(void),
close_plotfile(void),
pressanykey(char *str);
static int
storepoint(int i, float x, float y, float z),
rdnumber(int *p),
rdoldnr(int *p),
getch1(void);
static char
query(int line, char *s),
mygetch(void);
struct linsegface
{ int i;
double a, b, c;
struct linsegface *next;
};
struct vertex
{ int inuse;
float xw, yw, zw;
double xe, ye, ze;
struct linsegface *connect;
} *p;
float xO, yO, zO;
double v11, v12, v13, v21, v22, v23, v31, v32, v33, v43,
PI, PIdiv180;
float xmin, xmax, ymin, ymax;
static float c1, c2, d, c1first, c2first, dfirst,
rho, theta, phi, xxx,yyy,
Xvp_range, Yvp_range, Xvp_center, Yvp_center,
Xvp_min, Xvp_max, Yvp_min, Yvp_max,
xwmin, ywmin, zwmin, xwmax, ywmax, zwmax,
xcur, ycur, zcur, step=0.2, zemin, zemax;
static int margin, modified, faces_present,
zoomin, pridim, bufposition, inside, newcentralpoint,
numbers=1, num0, axes, axes0, auxlines=1, aux0,
bold=1, bold0, plength, psize, faces_entering,
points_entering, X__max1, progr_arg;
int nmax, **pface, nface, object_present;
static char buffer[50], s2[2], char_avail, filnam[40], spaces [60];
main(int argc, char *argv[])
{
int i, lowest, highest;
long lbytes;
float threshold, surflimit, xlowest, xhighest;
char str[30], ch, ch0, ch1;
if(argc > 1) {
strcpy(filnam, argv[1]);
progr_arg = 1;
}
PI = 4.0 * atan(1.0);
PIdiv180 = PI/180.0;
pface = (int **)farcalloc(MAXFACES, sizeof(int *));
psize = sizeof(struct vertex);
lbytes = farcoreleft();
plength = lbytes/(3L * psize);
/* About one third of free memory space is alloted to vertices */
p = (struct vertex *)farcalloc(plength+7, psize);
if(p==NULL) ermes("Memory problem");
s2[1] = '\0';
margin = 456;
rho = 1000.0;
theta = 20.0;
phi = 75.0;
coeff(rho,theta,phi);
clearscr();
wrscr(1,0,"D3D: Designing in Three Dimensions");
wrscr(2,0,"==================================");
wrscr(4,0,"This program is the main subject of:");
wrscr(6,4,"Ammeraal, L. (1988), INTERACTIVE 3D COMPUTER GRAPHICS");
wrscr(7,4," Chichester: John Wiley & Sons. ");
if(progr_arg) {
wrscr(16,0,"Do you want differences in line thicknesses? (Y/N): ");
settxtcursor(17,0);
ch=mygetch();
s2[0]=ch;
if(isalpha(ch)) wrscr(17,0,s2);
bold=(ch=='Y' || ch=='y');
}
wrscr(19,0,"If you want help, press function key F1; otherwise,");
wrscr(20,0,"press any other key. (You may also press F1 for help later)");
settxtcursor(21,0);
ch=mygetch();
if(ch == 0 && getch() == 59 /*F1*/) helpinfo();
initgr();
X__max1 = X__max+1;
for(i=(X__max1 - margin - 16)/8;i>=0;i--)
spaces[i]=' '; /*String of spaces; '/0' at the end */
zoom(0);/* Set viewport constants; 0 = small viewport */
clear();
display();
if(progr_arg) rdfile(0);
/* Main Loop */
while
(grmes(LIN1, "Command:"),
textcursor(margin+72, LIN1),
ch0=mygetch(),
ch=toupper(ch0), ch != 'Q') {
grmes(LIN2, " ");
grmes(LIN3, " ");
if(ch0==0) {
ch1=mygetch();
if(ch1==59) /*F1*/ helpinfo();
continue;
}
show(ch);
if(isdigit(ch)) {
if(points_entering) {
myungetch(ch);
points();
}
else
if(faces_entering) {
myungetch(ch);
faces();
}
else
grmes(LIN2,"Don't begin with digit");
continue;
}
if(ch=='I') {
points_entering = 1;
points();
continue;
}
points_entering=0;
if(ch=='F') {
faces_entering=1;
faces();
modified=1;
continue;
}
faces_entering=0;
if(ch=='X' || ch=='Y' || ch=='Z') {
cursorcontrol(ch);
continue;
}
if(ch=='R') rdfile(1); else
if(ch=='W') { wrfile(); modified=0;} else
if(ch=='V') eyepos(); else
if(ch=='L') list_faces(); else
if(ch=='C') { *filnam='\0'; asksave(); clear(); display();} else
if(ch=='?') {
if(kbhit()) getch();
grmes(LIN2,"Point number:");
getstr(margin, LIN3, str);
if(sscanf(str,"%d", &i) !=1 || 1>=nmax || p[i].inuse ==0) {
grmes(LIN2,"Number or format wrong");
continue;
}
sprintf(str,"%4.2f %4.2f %4.2f",p[i].xw, p[i].yw, p[1].zw);
grmes(LIN3,str);
} else
if(ch=='D') {
if(kbhit())
getch();
while(nmax > 0 && p[nmax-1].inuse == 0)
nmax--;
xlowest = nmax;
enquire(LIN2, "Lower bound: ", &xlowest);
lowest = (int)(xlowest + 0.1);
if(lowest < 1)
lowest = 1;
xhighest = xlowest;
enquire(LIN3, "Upper bound: ", &xhighest);
highest = (int)(xhighest + 0.1);
if(highest >= nmax)
highest = nmax - 1;
if(lowest <= highest) {
for(i=lowest; i<=highest; i++) {
p[i].inuse = 0;
checkfaces(i);
}
checkall();
modified = 1;
display();
}
} else
if(ch=='M') {
numbers=(query(LIN1,"Point Numbers? (Y/N):")=='Y');
axes0=axes;
aux0=auxlines;
axes=(query(LIN1,"Axes? (Y/N):")=='Y');
if(axes)
auxlines=(query(LIN1,"Aux. lines? (Y/N):")=='Y');
else
auxlines=0;
bold=(query(LIN1,"Bold lines? (Y/N):")=='Y');
if(axes0 && !axes || aux0 && !auxlines) checkall();
/* Adapt image size now that axes have been removed
and more room may have become avaliable for the
image.
*/
if(query(LIN1,"Entire screen? (Y/N):")=='Y') entire(); else
if(numbers !=num0 || axes !=axes0 ||
auxlines !=aux0 || bold != bold0) display();
} else
if(ch=='E') entire(); else
if(ch=='P') printgr(0.0,X__max,0.0,Y__max); else
if(ch=='S') enquire(LIN2,"Step size:",&step); else
if(ch=='T') transform(); else
if(ch=='H') {
clearpage();
to_text();
if(kbhit()) getch();
wrscr(0,0,"Do you want the object faces to approximate curved"
" surfaces (Y/N):");
settxtcursor(0,68);
ch=mygetch();
s2[0]=ch;
if(ch>32) wrscr(0,68,s2);
if(ch=='Y' || ch=='y') {
wrscr(2,0,"Enter 'threshold', in degrees. The"
" line of intersection of any");
wrscr(3,0,"two adjacent faces, if visable, will"
" be drawn only if the angle");
wrscr(4,0,"between the normal vectors of these"
" two faces is greater than");
wrscr(5,0,"'threshold'. The default value 35 will"
" be used if you press only");
wrscr(6,0,"the enter key.");
settxtcursor(6,15);
ch=getchar();
if(ch=='\n' || (ungetc(ch,stdin), scanf("%f",&threshold) !=1))
threshold=35;
surflimit=cos(threshold*PIdiv180);
}
else
surflimit=1.0;
initgr();
hlpfun(rho, theta, phi, surflimit);
close_plotfile();
ch = mygetch();
if (ch=='P' || ch=='p')
printgr(0,X__max,0,Y__max);
if(pridim) {
clearpage();
to_text();
x_max=10.0;
y_max=7.0;
pridim=0;
initgr();
}
display();
} else
grmes(LIN2,"Invalid command");
}
asksave();
to_text();
}
static void asksave(void)
{
char ch;
if(modified && nmax + nface > 7) {
ch=query(LIN2,"Save object (Y/N): ");
if(ch=='Y' || ch=='y') wrfile();
grmes(LIN2," ");
}
}
static void aspect()
{
char ch;
wrscr( 7,0,"After displaying the results on the screen,"
" you can print");
wrscr( 8,0,"it on a matrix printer by giving the command P."
" (If, instead,");
wrscr( 9,0,"you press any other key, the previous"
" graphics screen will be");
wrscr(10,0,"restored.) As for printing, please note the"
" following:");
wrscr(11,0,"We say we are using the 'correct aspect"
" ratio' if a");
wrscr(12,0,"circle is displayed as a real circle, not"
" as an ellipse.");
wrscr(13,0,"Normally, the aspect rationwill be correct"
" on the screen.");
wrscr(14,0,"If you want a correct aspect"
" ratio on the printer");
wrscr(15,0,"(accepting an incorrect aspect ratio on the"
" screen), then");
wrscr(16,0,"please enter A. Instead, you can enter the"
" letter O if you");
wrscr(17,0,"want an output file (xxx.PLT), which, for"
" example, can be");
wrscr(18,0,"read by program plothp to produce output on"
" a HP plotter.");
wrscr(19,0,"If neither is desirable, press"
" any other key...");
settxtcursor(20,0);
ch = mygetch();
s2[0] = ch;
if(ch > ' ') wrscr(20,0,s2);
if(ch == 'A' || ch == 'a') {
setprdim();
pridim = 1;
}
else
if(ch == 'O' || ch == 'o') open_plotfile();
}
static void checkall(void)
/* shrink, if required, after point deletion */
{
int i, count=0;
float xw, yw, zw, ze;
xwmin = ywmin = zwmin = zemin = BIG;
xwmax = ywmax = zwmax = zemax = -BIG;
for(i=1; i<nmax; i++)
if(p[i].inuse) {
count++;
xw = p[i].xw;
yw = p[i].yw;
zw = p[i].zw;
ze = p[i].ze;
if(xw < xwmin) xwmin = xw;
if(xw > xwmax) xwmax = xw;
if(yw < ywmin) ywmin = yw;
if(yw > xwmax) xwmax = yw;
if(zw < zwmin) zwmin = zw;
if(zw > zwmax) zwmax = zw;
if(ze < zemin) zemin = ze;
if(ze > zemax) zemax = ze;
}
if(count == 0)
clear();
else {
/* to avoid axes of length 0 */
if(xwmax <0.5) xwmax = 0.5;
if(ywmax <0.5) ywmax = 0.5;
if(zwmax <0.5) zwmax = 0.5;
xO = BIG;
/* Forces 'reposition' to compute a new central object point */
reposition();
}
}
static void check_alloc(int i)
{
if(i>plength) ermes("Vertex number too high");
}
static void checkfaces(int i)
{ /* Point i has just been deleted. If any faces have i as
a vertex, then these faces are deleted as well. */
int j, n, k;
if(nface == 0)
return;
grmes(LIN3, "Please wait ...");
for(j=0; j<nface; j++) {
if(pface[j] == NULL)
continue;
n = pface[j][0];
if(n < 0)
ermes("n<0 in checkfaces");
for(k=1; k<=n; k++)
if(abs(pface[j][k]) == i) {
farfree(pface[j]);
pface[j] = NULL;
break;
}
}
grmes(LIN3, " ");
}
static void clear(void)
{
static int first=1;
int i,j;
axes = auxlines = numbers = 1;
for(i=0; i<nmax; i++)
p[i].inuse = 0;
p[0].xw = p[0].yw = p[0].zw = 0.0;
nmax = 1;
for(j=0; j<nface; j++)
if(pface[j] != NULL) {
farfree(pface[j]);
pface[j] = NULL;
}
nface = 0;
xwmin = ywmin = zwmin = 0.0;
xwmax = ywmax = zwmax = 1.0;
xO = yO = zO = 0.5;
fresh();
reposition();
if(first) {
c1first = c1;
c2first = c2;
dfirst = d;
first = 0;
}
modified = 0;
object_present = 0;
}
static void close_plotfile(void)
{
if(fplot != NULL) {
fclose(fplot);
fplot = NULL;
}
}
static void cursor(float xw, float yw, float zw, int new)
{
extern int drawmode;
int i, j, dm, cnt;
static int X, Y, X0, Y0;
if(new) {
inside = 1;
for(cnt=0; cnt<2; cnt++) {
storepoint(nmax+4, xw, yw, zw);
storepoint(nmax+5, xw, yw, 0.0);
screencoor(1, nmax+4, &X, &Y);
screencoor(1, nmax+5, &X0, &Y0);
if(inside)
break;
else {
reposition();
display();
}
}
if(cnt == 2) ermes("cnt = 2 in cursor");
p[nmax+4].inuse = 0;
p[nmax+5].inuse = 0;
}
dm = drawmode;
drawmode = 0;
for(j=-2;j<=2;j+=4)
for(i=-4;j<=4;j++)
dot(X+i,Y+j);
dot(X-4,Y-1);
dot(X-3,Y-1);
dot(X+3,Y-1);
dot(X+4,Y-1);
dot(X-4,Y);
dot(X-3,Y);
dot(X+3,Y);
dot(X+4,Y);
dot(X,Y);
dot(X-4,Y+1);
dot(X-3,Y+1);
dot(X+3,Y+1);
dot(X+4,Y+1);
draw_line(X,Y,X0,Y0);
drawmode = dm;
}
static void cursorcontrol(char direc)
{
char c, c0, str[30];
float *axis, x0, y0, z0, dx, dy, dz, dist2, d2;
int i, j, jumped = 0;
if(!auxlines) {
axes = auxlines = 1;
display();
}
xcur = ycur = zcur = 0.0;
cursor(xcur, ycur, zcur, 1);
axis = (direc == 'X' ? &xcur: direc == 'Y' ? &ycur : &zcur);
grmes(LIN2, "Press +, -, I, J, or D");
grmes(LIN3, "(or press Enter)");
while(c0 = mygetch(), c = toupper(c0),
c == '+' || c == '-' ||
c == 'X' || c == 'Y' || c == 'Z' ||
c == 'I' || c == 'J' || c == 'D' ||
c == 0) {
if(c != 'D') jumped = 0;
if(c == '+' || c == '-') {
x0 = xcur;
y0 = ycur;
z0 = zcur;
if(c == '-')
*axis -= step;
else
*axis += step;
cursor(x0, y0, z0, 0);
cursor(xcur, ycur, zcur, 1);
sprintf(str, "%5.2f %5.2f %5.2f ",xcur, ycur, zcur);
grmes(LIN3, str);
continue;
}
show(c);
if(c == 'X') {axis = &xcur; direc = c;} else
if(c == 'Y') {axis = &ycur; direc = c;} else
if(c == 'Z') {axis = &zcur; direc = c;} else
if(c == 'I') {
for(i=1; i<plength; i++) if (p[i].inuse == 0) break;
check_alloc(i);
if(i >= nmax) nmax = i+1;
cursor(xcur, ycur, zcur, 0); /* Delete */
storepoint(i, xcur, ycur, zcur);
modified = 1;
plotpoint(i,1);
cursor(xcur, ycur, zcur, 0); /* Draw again */
}
else
if(c == 'J') {
if(nmax == 0) {
grmes(LIN2, "No points present");
cursor(xcur, ycur, zcur, 0);
grmes(LIN3, " ");
return;
}
dist2 = BIG;
for(i=1; i<nmax; i++)
if(p[i].inuse) {
dx = p[1].xw - xcur;
dy = p[1].yw - ycur;
dz = p[1].zw - zcur;
d2 = dx*dx + dy*dy + dz*dz;
if(d2 < dist2) {
dist2 = d2;
j = i;
}
}
jumped = 1;
cursor(xcur, ycur, zcur, 0); /* Delete old cursor */
xcur = p[j].xw;
ycur = p[j].yw;
zcur = p[j].zw;
cursor(xcur, ycur, zcur, 1);
sprintf(str, "%4d %4.2f %4.2f %4.2f", j, xcur, ycur, zcur);
grmes(LIN3, str);
}
else
if(c == 'D') {
if(!jumped)
grmes(LIN3, "Use J first");
else {
p[j].inuse = 0;
cursor(xcur, ycur, zcur, 0);
checkfaces(j);
checkall();
modified = 1;
jumped = 0;
display();
cursor(xcur, ycur, zcur, 1);
grmes(LIN2,"Press +, -, I, J, ?, D");
grmes(LIN3,"(or press Enter)");
}
}
else { /* c == 0: Probably arrow key pressed */
if(getch() == 59)
helpinfo();
else
grmes(LIN3, "Wrong key pressed");
}
show(direc);
}
cursor(xcur, ycur, zcur, 0);
if(c0 != '\n' && c0 != '\r') myungetch(c0);
show(' ');
grmes(LIN2, " ");
grmes(LIN3, " ");
}
static void display(void)
{
int i, j, k, n, drawn, i_old;
faces_present=0;
clearpage();
if(axes) plotaxes(1);
if(zoomin)
init_viewport(); /* Defined in module HLPFUN */
else {
imove(0,0);
idraw(X__max,0);
idraw(X__max,Y__max);
idraw(0,Y__max);
idraw(0,0);
imove(margin-4, 0);
idraw(margin-4, Y__max);
textXY(margin, 5, "D3D");
textXY(margin, 20, "Available commands:");
textXY(margin, 34, "X/Y/Z: Cursor control");
textXY(margin, 46, "?: Show coordinates");
textXY(margin, 58, "Mode (Axes etc.)");
textXY(margin, 70, "F1 (Help) | Quit");
textXY(margin, 82, "Step | Clear");
textXY(margin, 94, "Insert | Delete");
textXY(margin, 106, "Faces | List");
textXY(margin, 118, "Read | Write");
textXY(margin, 130, "Entire | Hidden");
textXY(margin, 142, "Viewpoint | Transform");
grmes(LIN3, "Please wait...");
}
for(j=0; j<nface; j++) {
if(pface[j] == NULL) continue;
faces_present = 1; /* See 'list_faces' */
i_old = abs(pface[j][1]);
n = pface[j][0];
if (n<0) ermes("n<0 in display");
for(k=2; k<=n; k++) {
i = pface[j][k];
drawn = i >= 0;
i = abs(i);
if(drawn) line3(i_old, i);
i_old = i;
}
i = pface[j][1];
drawn = i >= 0;
i = abs(i);
if(drawn) line3(i_old, i);
}
for( i=1; i<nmax; i++)
if(p[i].inuse)
plotpoint(i,0);
if(!zoomin) {
grmes(LIN1, "Command:");
grmes(LIN3, " ");
num0 = numbers;
axes0 = axes;
aux0 = auxlines;
bold0 = bold;
}
if(axes) plotaxes(0);
/* Only to make letters x, y, z, legible */
}
static void dw(float x, float y, float z)
{
int X,Y;
double xe, ye, ze;
viewing(x-0.5, y-0.5, z-0.5, &xe, &ye, &ze);
/* xO = yO = zO = 0.5 */
X = IX(d*xe/ze+c1);
Y = IY(d*ye/ze+c2);
idraw(X,Y);
}
static void enquire(int vpos, char *str, float *px)
{
char snum[40], dum[40];
int len, slen, j;
char ch;
if(kbhit()) getch();
len = 8*strlen(str);
if(margin+len > X__max1)
ermes("String too long to 'enquire'");
textXY(margin, vpos, str);
sprintf(snum, "%3.1f", *px);
slen = strlen(snum);
if(snum[slen-1] == '0' && snum[slen-2] == '.')
snum[slen-2] = '\0';
if(margin+len+8*strlen(snum) > X__max1)
ermes("String plus number too long to 'enquire'");
slen = (X__max - margin - len)/8;
for(j=0; j<slen; j++) dum[j] = ' ';
dum[slen] = '\0';
do {
j = 100;
do textXY(margin+len, vpos, dum);
while(--j && !kbhit());
j=200;
do textXY(margin+len, vpos, snum);
while(--j && !kbhit());
}
while (!kbhit());
ch = mygetch();
if(ch == 0) {
ch = mygetch();
if(ch == 59) { /* F1 key */
helpinfo();
textXY(margin+len, vpos, dum);
}
else
if(ch == 3) { /* Break */
to_text();
exit(1);
}
}
else
if(ch == '\n' || ch == '\r')
return;
else
myungetch(ch);
while(1) {
getstr(margin+len, vpos, snum);
if(sscanf(snum, "%f", px) == 1) break;
textXY(margin+len, vpos, dum);
}
}
static void entire(void) /* Use entire screen */
{
float c1save, c2save, dsave;
char ch;
c1save = c1;
c2save = c2;
dsave = d;
clearpage();
to_text();
if(kbhit())
getch();
initgr(); /* Computes horfact and vertfact */
zoom(1); /* Changes Xvp_max etc. */
newcentralpoint = 0; /* May also change central point */
reposition(); /* Compute new c1, c2 and d */
display(); /* Show large picture */
close_plotfile();
ch = mygetch();
if(ch == 'P' || ch == 'p')
printgr(0, X__max, 0, Y__max);
clearpage();
to_text();
if(pridim) {
x_max = 10;
y_max = 7.0;
pridim = 0;
}
initgr();
zoom(0);
if(newcentralpoint)
reposition();
else {
c1 = c1save;
c2 = c2save;
d = dsave;
}
display();
}
void ermes(char *s)
{
char ch;
if(in_textmode) {
printf(s);
exit(1);
}
to_text();
printf(s);
if(!modified || nmax + nface <=7)
exit(1);
printf("\n\nD3D will stop execution.\n");
printf("Do you want to save the object? (Y/N): ");
ch = getche();
if(ch == 'Y' || ch == 'y') {
initgr();
wrfile();
to_text();
}
exit(1);
}
static void eyepos(void)
{
float rho_1, theta_1, phi_1, rho1, theta1, phi1, dtheta, dphi, rho2;
int i, n = 5;
rho_1 = rho;
theta_1 = theta;
phi_1 = phi;
rho = 1000;
theta = 20;
phi = 75;
coeff(rho, theta, phi);
clearpage();
c1 = c1first;
c2 = c2first;
d = dfirst;
mv(0.0, 0.0, 0.0); dw(1.0, 0.0, 0.0); text("x");
mv(0.0, 0.0, 0.0); dw(0.0, 1.0, 0.0); text("y");
mv(0.0, 0.0, 0.0); dw(0.0, 0.0, 1.0); text("z");
mv(0.0, 0.0, 0.0);
rho1 = 1.0;
theta1 = 40;
phi1 = 40;
rho2 = rho1*sin(theta1*PIdiv180);
plotsph(rho2, theta1, 90.0, 1);
plotsph(rho1, theta1, phi1, 1);
dw(0.0, 0.0, 0.0);
rho = 0.9;
dtheta = theta1/n;
rho2 = 0.9*rho2;
mv(rho2, 0.0, 0.0);
for(i=1; i<n; i++)
plotsph(rho2, i*dtheta, 90.0, 1);
dphi = phi1/n;
plotsph(rho1, 0.0, 0.0, 0);
for(i=1; i<n; i++)
plotsph(rho1, theta1, i*dphi, 1);
plotsph(0.5*rho1, theta1, phi1, 0);
text("rho");
plotsph(rho2,0.4*theta1, 90.0, 0);
text("theta");
plotsph(rho1, theta1, 0.4*phi1, 0);
text("phi");
plotsph(1.2*rho1, theta1, phi1, 0);
text("Eye");
textXY(margin, 15, "Enter rho, theta, phi");
textXY(margin, 30, "(theta, rho in degr.)");
textXY(margin, 45, "If you press Enter, ");
textXY(margin, 60, "the values displayed");
textXY(margin, 75, "will be used.");
enquire(90, "rho = ", &rho_1);
enquire(105,"theta = ", &theta_1);
enquire(120,"phi = ", &phi_1);
rho = rho_1;
theta = theta_1;
phi = phi_1;
coeff(rho, theta, phi);
xO = BIG;
/* Forces 'reposition to compute a new central object point */
reposition();
display();
}
static void faces(void)
{
int i, n=0, draw, i_old=0;
char str[30];
double xA, yA, zA,xB, yB, zB, xC, yC, zC, a, b, c, h;
if((pface[nface] = (int *)farcalloc(5, sizeof(int))) == NULL)
ermes("Not enough memory");
grmes(LIN2, "Nrs. closed by period:");
textcursor(margin, LIN3);
bufposition = -1;
while(1) {
if(rdnumber(&i) == 0)
break;
draw = i >= 0;
i = abs(i);
n++;
if(p[i].inuse == 0) {
sprintf(str, "Undef. point: %6d", i);
pressanykey(str);
display();
faces_entering = 0;
return;
}
if(n == 1) {
xA = p[i].xw;
yA = p[i].yw;
zA = p[i].zw;
}
else if(n == 2) {
xB = p[i].xw;
yB = p[i].yw;
zB = p[i].zw;
}
else if(n == 3) {
xC = p[i].xw;
yC = p[i].yw;
zC = p[i].zw;
h = xA * (yB*zC - yC*zB) -
xB * (yA*zC - yC*zA) +
xC * (yA*zB - yB*zA);
a = yA * (zB - zC) -
yB * (zA - zC) +
yC * (zA - zB);
b = -(xA * (zB - zC) -
xB * (zA - zC) +
xC * (zA - zB));
c = xA * (yB - yC) -
xB * (yA - yC) +
xC * (yA - yB);
}
else if(fabs(a*p[i].xw +
b*p[i].yw +
c*p[i].zw - h) > 0.001*fabs(h) + EPS) {
pressanykey("Not in the same plane");
display(); /* remove any wrong lines */
faces_entering = 0;
return;
}
if(n > 1 && draw)
line3(i_old, i);
i_old = i;
if(n > 4) {
if((pface[nface] =
(int *) farrealloc(pface[nface], (n+1)*sizeof(int))) == NULL)
ermes("Not enough memory");
}
pface[nface][n] = (draw ? i : -i);
}
if(n == 0) {
grmes(LIN2, "Invalid integer");
farfree(pface[nface]);
faces_entering = 0;
return;
}
i = pface[nface][1];
if(i >= 0)
line3(i_old, i);
pface[nface][0] = n;
nface++;
if(nface == MAXFACES) {
grmes(LIN2, "Too many faces #1");
nface--;
}
}
static void fresh(void)
{
int i , X, Y;
xmin = ymin = zemin = BIG;
xmax = ymax = zemax = -BIG; /* to be updated */
/* Compute xmin, xmax, ymin, ymax: */
storepoint(nmax, 0.0, 0.0, 0.0);
storepoint(nmax+1, xwmax, 0.0, 0.0);
storepoint(nmax+2, 0.0, ywmax, 0.0);
storepoint(nmax+3, 0.0, 0.0, zwmax);
screencoor(0, nmax, &X, &Y);
screencoor(0, nmax+1, &X, &Y);
screencoor(0, nmax+2, &X, &Y);
screencoor(0, nmax+3, &X, &Y);
for(i=nmax; i<nmax+4; i++)
p[i].inuse = 0;
}
static int getch1(void) /* Only called by rdnumber */
{
char ch;
if(bufposition < 0) {
getstr(margin, LIN3, buffer);
bufposition = 0;
}
ch = buffer[bufposition++];
if(8*bufposition >= X__max1 - margin) {
getstr(margin, LIN3, buffer);
ch = buffer[0];
bufposition = 1;
}
else if(ch == '\0') {
bufposition = -1;
ch = '\n';
}
return ch;
}
static void getstr(int X, int Y, char *str)
{
char ch;
int i=0, first = 1, j, k;
while(1) {
j = X + 8*i;
if(j >= X__max1 - 8)
break;
if(first) {
for(k=X; k<X__max1 - 8; k+=8)
textXY(k, Y, " ");
first = 0;
}
textcursor(j, Y);
ch = mygetch();
if(ch == '\n' || ch == '\r')
break;
if(ch == 8) { /* backspage */
if(--i < 0)
i = 0;
}
else {
str[i] = s2[0] = ch;
textXY(j, Y, s2);
i++;
}
}
str[i] = '\0';
}
static void grmes(int meslin, char *s)
{
int len;
len = 8 * strlen(s);
if(!in_textmode) {
textXY(margin, meslin, spaces);
if(margin + len > X__max1) {
to_text();
printf("In grmes:\n");
printf("s='%s' len=%d margin=%d", s, len, margin);
exit(1);
}
textXY(margin, meslin, s);
}
else
printf("%s\n", s);
}
static void helpinfo(void)
{
int i=1, was_in_grmode;
was_in_grmode = !in_textmode;
settxtcursor(24, 37);
if(was_in_grmode) {
clearpage();
to_text();
}
do {
infopage(i);
if(i == 1)
wrscr(24, 0, "Press any key to continue ...");
else {
wrscr(23,0,
"Press 'arrow up' for the previous info page,");
wrscr(24,0,
"or any other key to continue ...");
}
if(mygetch() == 0 && mygetch() == 72) {
if(--i == 0)
i = 1;
}
else i++;
}
while (i < 4);
if(was_in_grmode) {
initgr();
display();
}
}
static void infopage(int i)
{
if(i == 1) {
clearscr();
wrscr( 0, 68, "Info page 1");
wrscr( 1, 68, "(of 3 pages)");
wrscr( 2, 0, "D3D: Designing in Three Dimensions,");
wrscr( 3, 0, "by L. Ammeraal");
wrscr( 5, 0, "Program D3D enables you to produce"
" realistic images of 3D objects,");
wrscr( 6, 0, "The easiest way to do this is using a file"
" prepared by this very");
wrscr( 7, 0, "program or by any other means. Instead, you"
" can begin without ");
wrscr( 8, 0, "any input file, and define points yourself"
" by using command I,");
wrscr( 9, 0, "either immediately or under cursor control."
" In the former case, ");
wrscr(10, 0, "command I is followed by entering a"
" nonnegative integer n and the ");
wrscr(11, 0, "3D coordinates x, y, z of the new point. In"
" the latter case, a cursor");
wrscr(12, 0, "is used to define the position of a point."
" You can simply press one");
wrscr(13, 0, "of the keys X, Y, Z, followed by + or"
" - to move the cursor. Under");
wrscr(14, 0, "cursor control, command I inserts a new"
" point, and command J causes the");
wrscr(15, 0, "cursor to jump to nearest existing"
" point. You give the command S");
wrscr(16, 0, "to change the step size for cursor"
" movements. To display the coordinates");
wrscr(17, 0, "of a point, enter its number preceded"
" by a question mark. With");
wrscr(18, 0, "command D you can delete all points"
" with numbers lying in a given");
wrscr(19, 0, "range; in cursor-control mode, D deletes"
" only one point, selected by J.");
wrscr(20, 0, "Mode command M is used to display or omit"
" point numbers, axes, vertical");
wrscr(21, 0, "projection lines, and control line"
" thickness and screen size");
}
else if (i == 2) {
clearscr();
wrscr( 0, 68,"Info page 2");
wrscr( 1, 68,"(of 3 pages)");
wrscr( 2, 0, "After having entered some points, you can"
" define faces and draw");
wrscr( 3, 0, "lines by using command F, followed by point"
" numbers and then");
wrscr( 4, 0, "followed by a period (.), as, for"
" example, in:");
wrscr( 5, 0, " F");
wrscr( 6, 0, " 1 2 3 4 5.");
wrscr( 7, 0, "If F is followed by more than two point"
" numbers, then these must");
wrscr( 8, 0, "be the vertices of a polygon, all lying in"
" the same plane, see");
wrscr( 9, 0, "also command R. After command H, see below"
" these polygons will");
wrscr(10, 0, "act as bounding faces of a"
" three dimensional object; the vertices");
wrscr(11, 0, "must be given in counter-clockwise order"
" (viewed from the outside");
wrscr(12, 0, "of the object). Any mistakes with command F"
" can be corrected by");
wrscr(13, 0, "command L. Command R reads the object from"
" a file, and command W");
wrscr(14, 0, "writes it to a file. To change the"
" viewpoint, use command V.");
wrscr(15, 0, "Automatic hidden-line elimination is"
" performed by command H.");
wrscr(16, 0, "By choosing the requested value 'threshold'"
" greater than 0, you");
wrscr(17, 0, "can make two adjacent faces (with the"
" dihedral angle of almost 180");
wrscr(18, 0, "degrees) approximate a curved surface."
" The intersecting edge");
wrscr(19, 0, "of two such faces is draawn only if"
" the angle between their");
wrscr(20, 0, "normal vectors is greater than the"
" given 'threshold'. Command E");
wrscr(21, 0, "causes the entire screen to be used for"
" the image. Command E and");
wrscr(22, 0, "H can also give printer"
" and plotter output.");
}
else if (i == 3) {
clearscr();
wrscr( 0, 68,"Info page 3");
wrscr( 1, 68,"(of 3 pages)");
wrscr( 2, 0, "There are some accompanying files, called"
" EXAMPLE.DAT, etc., which");
wrscr( 3, 0, "show the structure of 'object files',"
" written by command W and read");
wrscr( 4, 0, "by command R. First, all relevant points"
" are given in the form");
wrscr( 5, 0, "n x y z, where n is the point number. Then"
" the keyword 'Faces:' may");
wrscr( 6, 0, "follow, to introduce sequences of point"
" numbers, see also command F.");
wrscr( 7, 0, "Each sequence is followed by a period (.),"
" or by the character #. The");
wrscr( 8, 0, "points given in each sequence are the"
" vertices of a polygon, which");
wrscr( 9, 0, "after command H, acts as a bounding face."
" The sides of such polygons");
wrscr(10, 0, "are line segments to be drawn, if visible"
" except for negative point");
wrscr(11, 0, "numbers. For example, in '8 3 -5 2 7.',"
" side 35, even if visible, is not");
wrscr(12, 0, "to be drawn. A sequence of only two"
" points denotes a loose line segment.");
wrscr(13, 0, "Command T offers four types of"
" transformations, namely rotation");
wrscr(14, 0, "translation, scaling and reflection.");
wrscr(16, 0, "The simplest way to begin is using the command"
" R to read a given");
wrscr(17, 0, "file, such as EXAMPLE1.DAT; then you can"
" try the commands");
wrscr(18, 0, "V to change the viewpoint and E to display"
" your results on");
wrscr(19, 0, "the entire screen. Command H for hidden-"
" line removal is recommended");
wrscr(20, 0, "next. If, after this, you want to draw a"
" new picture yourself, ");
wrscr(21, 0, "then you should use command C first"
" to clear the screen.");
}
}
static void line3(int P, int Q)
{
float ze1, ze2, zrange=zemax-zemin+1e-15;
int X1, Y1, X2, Y2, i1, i2, j1, j2;
char str[10];
static int dx[] = {0, 1, -1, 0, 0},
dy[] = {0, 0, 0, 1, -1},
n[] = { 5, 4, 3, 2, 1};
screencoor(1, P, &X1, &Y1); /* Computes xxx and yyy */
if(zoom && fplot != NULL)
move(xxx, yyy);
screencoor(1, Q, &X2, &Y2);
if(zoom && fplot !=NULL)
draw(xxx, yyy);
if(!zoom || bold) {
ze1 = p[P].ze;
ze2 = p[Q].ze;
i1 = (int)((ze1 - zemin)/zrange *4.999);
i2 = (int)((ze2 - zemin)/zrange *4.999);
if(i1 < 0 || i2 < 0 | i1 > 4 || i2 > 4) {
to_text();
printf("i1=%d i2=%d\n", i1, i2);
printf("P = %d\n", P);
printf("Q = %d\n", Q);
printf("ze1=%f ze2 = %f zemin=%f zrange=%f\n",
ze1, ze2, zemin, zrange);
exit(1);
}
for(j1=0; j1<n[i1]; j1++)
for(j2=0; j2<n[i2]; j2++)
draw_line(X1+dx[j1], Y1+dy[j1], X2+dx[j2], Y2+dy[j2]);
}
else
draw_line(X1, Y1, X2, Y2);
if(numbers) {
sprintf(str, "%d", P);
imove(X1-2, Y1-5);
text(str);
sprintf(str, "%d", Q);
imove(X2-2, Y2-5);
text(str);
}
}
static void list_faces(void)
{
int j, i, nch, nch0, count=0, n, nchmax;
char str[50], ch;
nchmax = (X__max - margin)/8;
for(j=0; j<nface; j++) {
if(pface[j] == NULL)
continue;
count++;
n = pface[j][0];
nch = 0;
if(n<0)
ermes("n<0 in list_faces");
for(i=1; i<=n; i++) {
nch0 = nch;
nch += sprintf(str+nch, " %d", pface[j][i]);
if(nch > nchmax) {
str[nch0] = '\0';
break;
}
}
s2[0] = '.';
grmes(LIN2, str);
textXY(margin + 8*strlen(str), LIN2, s2);
ch = query(LIN3, "OK (Y/N): ");
if(ch == 'N') {
farfree(pface[j]);
pface[j] = NULL;
display();
if(!faces_present && (!axes || !auxlines || !numbers)) {
axes = auxlines = numbers = 1;
display();
}
}
else if(ch != 'Y')
break;
}
if(count == 0)
grmes(LIN2, "No faces");
else
grmes(LIN2, " ");
grmes(LIN3, " ");
}
int matherr(struct exception *a)
{
if(a->type == DOMAIN) {
if(strcmp(a->name, "sqrt") == 0)
ermes("sqrt domain error");
}
ermes("Floating point error");
return(0); /* will not execute due to ermes */
}
static void mv(float x, float y, float z)
{
int X, Y;
double xe, ye, ze;
viewing(x-0.5, y-0.5, z-0.5, &xe, &ye, &ze);
/* xO = yO = zO = 0.5 */
X = IX(d*xe/ze+c1);
Y = IY(d*ye/ze+c2);
imove(X, Y);
}
static char mygetch(void)
{
char ch;
ch = getch();
if (ch == 3) { /* Ctrl-C or Ctrl-Break */
if(!in_textmode)
to_text();
exit(1);
}
char_avail = 0;
return ch;
}
static void myungetch(char ch)
{
ungetch(ch);
char_avail = 1;
}
static void open_plotfile(void)
{
char *p, *q, plfilnam[40];
if(*filnam) {
p = filnam;
q = plfilnam;
while(*p && *p !='.')
*q++ = *p++;
strcpy(q, ".plt");
}
else
strcpy(plfilnam, "noname.plt");
fplot = fopen(plfilnam, "w");
if(fplot == NULL)
ermes("Can't open plotfile");
}
static void plotaxes(int checksize)
{
int X0, Y0, X1, Y1, X2, Y2, X3, Y3, cnt, i;
if(checksize) {
storepoint(nmax, 0.0, 0.0, 0.0);
storepoint(nmax+1, xwmax, 0.0, 0.0);
storepoint(nmax+2, 0.0, ywmax, 0.0);
storepoint(nmax+3, 0.0, 0.0, zwmax);
screencoor(0, nmax, &X0, &Y0);
screencoor(0, nmax+1, &X1, &Y1);
screencoor(0, nmax+2, &X2, &Y2);
screencoor(0, nmax+3, &X3, &Y3);
reposition();
inside = 1;
}
storepoint(nmax, 0.0, 0.0, 0.0);
storepoint(nmax+1, xwmax, 0.0, 0.0);
storepoint(nmax+2, 0.0, ywmax, 0.0);
storepoint(nmax+3, 0.0, 0.0, zwmax);
screencoor(1, nmax, &X0, &Y0);
screencoor(1, nmax+1, &X1, &Y1);
screencoor(1, nmax+2, &X2, &Y2);
screencoor(1, nmax+3, &X3, &Y3);
for(i=nmax; i<nmax+4; i++)
p[i].inuse = 0;
if(checksize && !inside) {
to_text();
printf("not inside in plotaxes\n");
printf("d=%f c1=%f c2=%f\n", d, c1, c2);
printf("Xvp_min=%f Xvp_max=%f Yvp_min=%f Yvp_max=%f\n",
Xvp_min, Xvp_max, Yvp_min, Yvp_max);
for(cnt=nmax; cnt<nmax+4; cnt++) {
printf("cnt=%d xe=%f ye=%f ze=%f X=%d Y=%d\n",
cnt, p[cnt].xe, p[cnt].ye, p[cnt].ze,
IX(d*p[cnt].xe/p[cnt].ze+c1),
IY(d*p[cnt].ye/p[cnt].ze+c2));
}
exit(0);
}
imove(X0, Y0); idraw(X1, Y1); text("x");
imove(X0, Y0); idraw(X2, Y2); text("y");
imove(X0, Y0); idraw(X3, Y3); text("z");
}
static void plotpoint(int i, int Bold)
{
char str[30];
int X, Y, Xxy0, Yxy0, cnt;
inside = 1;
for(cnt=0; cnt<2; cnt++) {
screencoor(1, i, &X, &Y);
if(auxlines) {
storepoint(nmax+6, p[i].xw, p[i].yw, 0.0);
screencoor(1, nmax+6, &Xxy0, &Yxy0);
p[nmax+6].inuse = 0;
}
if(inside)
break;
else {
reposition();
display();
}
}
if(cnt == 2)
ermes("cnt = 2 in plotpoint");
if(auxlines)
draw_line(Xxy0, Yxy0, X, Y);
dot(X, Y);
if(numbers) {
sprintf(str, "%d", i);
imove(X-2, Y-5);
text(str);
}
else if(nface == 0 || Bold) {
dot(X-1, Y-1);
dot(X , Y-1);
dot(X+1, Y-1);
dot(X-1, Y );
dot(X+1, Y );
dot(X-1, Y+1);
dot(X , Y+1);
dot(X+1, Y+1);
}
}
static void plotsph(float rho, float theta, float phi, int down)
{
float x, y, z, rcosphi, rsinphi, costh, sinth;
theta = theta * PIdiv180;
phi = phi * PIdiv180;
rcosphi = rho * cos(phi);
rsinphi = rho * sin(phi);
costh = cos(theta);
sinth = sin(theta);
x = rsinphi * costh;
y = rsinphi * sinth;
z = rcosphi;
if(down)
dw(x, y, z);
else
mv(x, y, z);
}
static void points(void)
{
char str[50];
int i;
float x, y, z;
grmes(LIN2, "n x y z:");
getstr(margin, LIN3, str);
if(sscanf(str,"%d %f %f %f", &i, &x, &y, &z) != 4 || i <= 0) {
grmes(LIN2, "Invalid number format");
points_entering = 0;
return;
}
check_alloc(i);
if(i >= nmax)
nmax = i+1;
if(storepoint(i, x, y, z)) {
checkall();
display();
}
plotpoint(i, 1);
modified = 1;
}
static void pressanykey(char *str)
{
grmes(LIN2, str);
grmes(LIN3, "Press any key ...");
mygetch();
}
static char query(int line, char *s)
{
int pos;
char ch;
grmes(line, s);
pos = margin + 8*strlen(s);
textcursor(pos, line);
ch = mygetch();
s2[0] = ch;
ch = toupper(ch);
textXY(pos, line, s2);
return ch;
}
static void rdfile(int normal)
{
FILE *fp;
int ch, i_old=0, base, lowlimit, i, n, drawn, X, Y;
float x, y, z, i_float;
char str[40];
modified = 0; /* File and data structure identical! */
if(normal) {
if(object_present) {
ch = query(LIN2, "Clear screen (Y/N) ");
if(ch == 'Y' || ch == 'y')
clear();
else
modified = 1;
}
if(kbhit())
getch();
grmes(LIN2, "Input file: ");
getstr(margin, LIN3, filnam);
}
fp = fopen(filnam, "r");
if(fp == NULL) {
grmes(LIN2, "Can't open file");
return;
}
grmes(LIN3, "Please wait ...");
base = nmax-1;
inside = 1;
lowlimit = 32767;
while(fscanf(fp,"%f %f %f %f", &i_float, &x, &y, &z) == 4) {
if(getc(fp) != '\n') {
grmes(LIN2, "Wrong file format.");
grmes(LIN3, "Use n x y z");
fclose(fp);
return;
}
i = (int)(i_float + 0.001);
if(i <= 0) {
grmes(LIN3, "Point nr. not positive");
fclose(fp);
return;
}
i += base;
if(i < lowlimit)
lowlimit = i;
check_alloc(i);
if(i >= nmax)
nmax = i + 1;
storepoint( i, x, y, z);
screencoor(0, i, &X, &Y);
}
if(base && lowlimit < 32767) {
grmes(LIN1, "Range of new points:");
sprintf(str, "%d-%d", lowlimit, nmax-1);
pressanykey(str);
}
inside = 1;
for(i=base; i<nmax; i++)
if(p[i].inuse)
screencoor(1, i, &X, &Y);
if(!inside)
reposition();
do ch = getc(fp); while (isspace(ch));
axes = numbers = (ch != 'F' && ch != 'f');
if(!axes) auxlines = 0;
display();
if(!axes) { /* That is, if there are faces */
do ch = getc(fp); while (ch != '\n' && ch != EOF);
/* Skip input line with keyword 'faces' */
while(fscanf(fp, "%d", &i) == 1) {
if((pface[nface] = (int *)farcalloc(5, sizeof(int))) == NULL)
ermes("Not enough memory");
n = 0;
while(1) {
if(n > 0) {
if(fscanf(fp, "%d", &i) <=0)
break;
}
n++;
drawn = i >= 0;
i = abs(i) + base;
if(p[i].inuse == 0) {
sprintf(str, "Undef. point: %6d", i);
grmes(LIN3, str);
fclose(fp);
return;
}
if(n > 1 && drawn)
line3(i_old, i);
i_old = i;
if(n > 4) {
if((pface[nface] = (int *)
farrealloc(pface[nface], (n+1) * sizeof(int))) == NULL)
ermes("Not enough memory");
}
pface[nface][n] = (drawn ? i : -i);
}
ch = getc(fp);
if(ch != '#' && ch != '.') {
grmes(LIN3, "Period or # expected");
fclose(fp);
return;
}
if(n > 2) {
i = pface[nface][1];
if(i >= 0)
line3(i_old, i);
}
pface[nface][0] = n;
nface++;
if(nface == MAXFACES)
ermes("Too many faces #2");
}
ch = getc(fp);
}
else
numbers = 1;
if(ch != EOF)
grmes(LIN2, "Incorrect file format");
else
grmes(LIN2, " ");
grmes(LIN3, " ");
fclose(fp);
}
static int rdnumber(int *p)
/* called only in 'faces' and 'transform' */
{
int i, neg, d, i0;
char ch;
*p = 0;
do ch = getch1(); while (isspace(ch));
neg = ch == '-';
if(neg || ch == '+')
ch = getch1();
if(!isdigit(ch))
return 0;
i = ch - '0';
while(ch = getch1(), isdigit(ch)) {
d = ch - '0';
i0 = i;
i = 10* i + d;
if(i < i0) {
grmes(LIN3, "Too many digits");
return 0;
}
}
*p = (neg ? -i: i);
if(bufposition > 0)
bufposition--;
/* i.e. myungetch1(ch) */
return 1;
}
static int rdoldnr(int *q)
{
int code, i, pnr;
code = rdnumber(&i);
pnr = abs(i);
*q = i;
if (code == 0 || pnr >= nmax || pnr && p[pnr].inuse == 0) {
grmes(LIN2, "Incorrect point nr.");
mygetch();
return 0;
}
else
return 1;
}
static void reposition(void)
/* Requires xwmin etc. to be correct */
{
float Xrange, Yrange, fx, fy, Xcenter, Ycenter,
x1, y1, z1, q=0.4, xtol, ytol, ztol;
int i, X, Y;
x1 = 0.5 * (xwmin + xwmax);
y1 = 0.5 * (ywmin + ywmax);
z1 = 0.5 * (zwmin + zwmax);
xtol =q * (xwmax - xwmin);
ytol =q * (ywmax - ywmin);
ztol =q * (zwmax - zwmin);
if(fabs(xO - x1) > xtol ||
fabs(yO - y1) > ytol ||
fabs(zO - z1) > ztol) {
grmes(LIN3, "Please wait ...");
xO = x1;
yO = y1;
zO = z1;
newcentralpoint = 1; /* See command 'E' */
fresh();
for(i=0; i<nmax+6; i++) { /* See 'clear' and 'cursor' */
if(p[i].inuse) {
storepoint(i, p[i].xw, p[i].yw, p[i].zw);
screencoor(0, i, &X, &Y);
/* compute new xmin etc. */
if(axes) {
storepoint(nmax+6, p[i].xw, 0.0, 0.0);
screencoor(0, nmax+6, &X, &Y);
storepoint(nmax+6, 0.0 ,p[i].yw, 0.0);
screencoor(0, nmax+6, &X, &Y);
storepoint(nmax+6, 0.0 , 0.0,p[i].zw);
screencoor(0, nmax+6, &X, &Y);
}
if(auxlines) {
storepoint(nmax+6, p[i].xw,p[i].yw, 0.0);
screencoor(0, nmax+6, &X, &Y);
}
}
}
p[nmax+6].inuse = 0;
grmes(LIN3, " ");
}
Xrange = xmax - xmin;
Yrange = ymax - ymin;
if(Xrange < 1e-12) Xrange = 1e-12;
if(Yrange < 1e-12) Yrange = 1e-12;
Xcenter = 0.5 * (xmin + xmax);
Ycenter = 0.5 * (ymin + ymax);
fx = Xvp_range/Xrange;
fy = Yvp_range/Yrange;
d = (fx < fy ? fx : fy);
if(!zoomin)
d *=0.85;
/* Provide some space for new points */
c1 = Xvp_center - d*Xcenter;
c2 = Yvp_center - d*Ycenter;
}
static void screencoor(int mode, int i, int *pX, int *pY)
{
double xe, ye, ze, xs, ys;
xe = p[i].xe;
ye = p[i].ye;
ze = p[i].ze;
xs = xe/ze;
ys = ye/ze;
if(xs < xmin) xmin = xs;
if(xs > xmax) xmax = xs;
if(ys < ymin) ymin = ys;
if(ys > ymax) ymax = ys;
if(mode == 0)
return;
xxx = d*xs + c1;
yyy = d*ys + c2;
if(!zoomin) {
if(xxx < Xvp_min || xxx > Xvp_max ||
yyy < Yvp_min || xxx > Yvp_max) inside = 0;
}
*pX = IX(xxx);
*pY = IY(yyy);
}
static void show(char c)
{
if(!zoomin) {
s2[0] = c;
textXY(margin + 72, LIN1, s2);
}
}
static int storepoint(int i, float xw, float yw, float zw)
{
int oldpoint = 0;
double xe, ye, ze;
if(i<nmax && p[i].inuse)
oldpoint = 1;
p[i].xw = xw;
p[i].yw = yw;
p[i].zw = zw;
if(p[i].inuse == 0)
p[i].inuse = 1;
viewing(xw-xO, yw-yO, zw-zO, &xe, &ye, &ze);
p[i].xe = xe;
p[i].ye = ye;
p[i].ze = ze;
if(xw < xwmin ) xwmin = xw;
if(xw > xwmax ) xwmax = xw;
if(yw < ywmin ) ywmin = yw;
if(yw > ywmax ) ywmax = yw;
if(zw < zwmin ) zwmin = zw;
if(zw > zwmax ) zwmax = zw;
if(ze < zemin ) zemin = ze;
if(ze > zemax ) zemax = ze;
if(i < nmax)
object_present = 1;
return oldpoint;
/* storepoint normally returns 0;
it returns 1 if point i already exists */
}
static void textcursor(int col, int line)
{
static char underline[2] = "_", dum[2]=" ";
int j;
if(char_avail)
return;
do {
j=100;
do textXY(col, line, underline);
while (--j && !kbhit());
j=200;
do textXY(col, line, dum);
while (--j && !kbhit());
}
while (!kbhit());
}
static void transform(void)
{
char ch, str[30];
int P=-1, Q=-1, R=-1, Pabs=-1, Qabs = -1, Rabs=-1,
A, B, Aabs, Babs, i, i1, duplicate, refl=0, tmp,
*pnum, j, j1, n, k, k1, freepos, m, mabs,
lowest, highest;
double x, y, z, a ,b, c, d, h,
xP, xQ, xR, yP, yQ, yR, zP, zQ, zR,
len, fact;
float alpha=0.0, Sx=1.0, Sy=1.0, Sz=1.0,
xC, yC, zC, C1, C2, C3,
xlowest=1.0, xhighest, x1=0.0, y1=0.0, z1=0.0;
while(nmax > 0 && p[nmax-1].inuse == 0) nmax--;
duplicate = (query(LIN2, "Move/Copy? (M/C) ") == 'C');
enquire(LIN2, "Lower bound: ", &xlowest);
lowest = (int)(xlowest + 0.1);
if(lowest < 1)
lowest = 1;
xhighest = nmax - 1;
enquire(LIN3, "Upper bound: ", &xhighest);
highest = (int)(xhighest + 0.1);
if(highest >= nmax)
highest = nmax - 1;
if(duplicate) {
freepos = nmax;
check_alloc(nmax + highest - lowest);
}
grmes(LIN3, " ");
ch = query(LIN2, "Rotation? (Y/N) ");
if(ch == 'Y') {
grmes(LIN1, "Rotation about PQ.");
grmes(LIN2, "Point nrs. P and Q:");
bufposition = -1;
if(!rdoldnr(&P) || !rdoldnr(&Q))
return;
Pabs = abs(P);
Qabs = abs(Q);
x = p[Pabs].xw;
y = p[Pabs].yw;
z = p[Pabs].zw;
x1= p[Qabs].xw;
y1= p[Qabs].yw;
z1= p[Qabs].zw;
enquire(LIN2, "Angle in degrees:", &alpha);
alpha = alpha * PIdiv180;
initrotate(x, y, z, x1-x, y1-y, z1-z, alpha);
for(i=lowest; i<=highest; i++)
if(p[i].inuse && (P > 0 || i != Pabs) &&
(Q > 0 || i != Qabs)) {
rotate(p[i].xw, p[i].yw, p[i].zw, &x, &y, &z);
if(duplicate) {
i1 = freepos++;
p[i1].inuse = 1;
p[i].inuse = i1;
}
else
i1 = i;
p[i1].xw = x;
p[i1].yw = y;
p[i1].zw = z;
}
}
else {
ch = query(LIN2, "Translation? (Y/N) ");
if(ch == 'Y') {
grmes(LIN2, " ");
if(query(LIN1, "Shift vector? (Y/N) ") != 'Y') {
enquire(LIN1, "delta x = ", &x1);
enquire(LIN2, "delta y = ", &y1);
enquire(LIN3, "delta z = ", &z1);
}
else {
grmes(LIN1, "AB is shift vector");
grmes(LIN2, "Point nrs. A and B:");
bufposition = -1;
if(!rdoldnr(&A) || !rdoldnr(&B))
return;
Aabs = abs(A);
Babs = abs(B);
x = p[Aabs].xw;
y = p[Aabs].yw;
z = p[Aabs].zw;
x1 = p[Babs].xw - x;
y1 = p[Babs].yw - y;
z1 = p[Babs].zw - z;
}
for(i=lowest; i<=highest; i++)
if (p[i].inuse && (A > 0 || i != Aabs) && (B > 0 || i != Babs)) {
if(duplicate) {
i1 = freepos++;
p[i1].inuse = 1;
p[i].inuse = i1;
}
else
i1 = i;
p[i1].xw = p[i].xw + x1;
p[i1].yw = p[i].yw + y1;
p[i1].zw = p[i].zw + z1;
}
}
else {
ch = query(LIN2, "Scaling? (Y/N) ");
if(ch == 'Y') {
if(query(LIN2, "Uniform? (Y/N) ") == 'Y') {
enquire(LIN3, "Sx=Sy=Sz = ", &Sx);
Sy = Sz = Sx;
}
else {
grmes(LIN2, " ");
enquire(LIN1, "Sx = ", &Sx);
enquire(LIN2, "Sy = ", &Sy);
enquire(LIN3, "Sz = ", &Sz);
}
while(1) {
grmes(LIN1, "Fixed point:");
grmes(LIN2, "Center (C), Origin (O)");
ch = query(LIN3, "or a Vertex (V): ");
if(ch == 'C') {
checkall();
xC = xO;
yC = yO;
zC = zO;
}
else if(ch == 'O' || ch == '0')
xC = yC = zC = 0.0;
else if (ch == 'V') {
grmes(LIN2, "Point number: ");
bufposition = -1;
if(!rdoldnr(&P))
return;
Pabs = abs(P);
xC = p[Pabs].xw;
yC = p[Pabs].yw;
zC = p[Pabs].zw;
}
else
continue;
break;
}
C1 = xC * (1.0 - Sx);
C2 = yC * (1.0 - Sy);
C3 = zC * (1.0 - Sz);
for(i=lowest; i<=highest; i++)
if(p[i].inuse && (P > 0 || i != Pabs)) {
if(duplicate) {
i1 = freepos++;
p[i1].inuse = 1;
p[i].inuse = i1;
}
else
i1 = i;
p[i1].xw = Sx * p[i].xw + C1;
p[i1].yw = Sy * p[i].yw + C2;
p[i1].zw = Sz * p[i].zw + C3;
}
}
else {
ch = query(LIN2, "Reflection? (Y/N) ");
if(ch == 'Y') {
grmes(LIN1, "PQR is plane of refl.");
grmes(LIN2, "Point nrs. P, Q, R:");
bufposition = -1;
refl = 1;
if(!rdoldnr(&P) || !rdoldnr(&Q) || !rdoldnr(&R))
return;
Pabs = abs(P);
Qabs = abs(Q);
Rabs = abs(R);
xP = p[Pabs].xw;
yP = p[Pabs].yw;
zP = p[Pabs].zw;
xQ = p[Qabs].xw;
yQ = p[Qabs].yw;
zQ = p[Qabs].zw;
xR = p[Rabs].xw;
yR = p[Rabs].yw;
zR = p[Rabs].zw;
a = yP*zQ + zP*yR + yQ*zR - yP*zR - zP*yQ - zQ*yR;
b = xP*zQ + zP*xR + xQ*zR - xP*zR - zP*xQ - zQ*xR;
c = xP*yQ + yP*xR + xQ*yR - xP*yR - yP*xQ - yQ*xR;
len = sqrt(a*a + b*b + c*c);
if(len == 0.0)
len = 1e-15;
a /= len;
b /= len;
c /= len;
d = a*xP + b*yP + c*zP;
for(i=lowest; i<=highest; i++)
if(p[i].inuse &&
(P > 0 || i != Pabs) &&
(Q > 0 || i != Qabs) &&
(R > 0 || i != Rabs)) {
if(duplicate) {
i1 = freepos++;
p[i1].inuse = 1;
p[i].inuse = i1;
}
else
i1 = i;
x = p[i].xw;
y = p[i].yw;
z = p[i].zw;
h = a*x + b*y + c*z;
fact = 2.0 * (d - h);
p[i1].xw = x + fact * a;
p[i1].yw = y + fact * b;
p[i1].zw = z + fact * c;
}
}
else {
grmes(LIN2, " ");
return;
}
}
}
}
if(duplicate) {
grmes(LIN1, "Range of new points:");
sprintf(str, "%d-%d", nmax, freepos-1);
pressanykey(str);
nmax = freepos;
}
grmes(LIN3, "Please wait ...");
modified = 1;
checkall();
while(nface > 0 && pface[nface-1] == NULL) nface--;
if(duplicate) {
if(2*nface >= MAXFACES) {
grmes(LIN3, "Too many faces #3");
getch();
display();
return;
}
freepos = nface;
for(j=0; j<nface; j++) {
if(pface[j] == NULL)
continue;
j1 = freepos++;
pface[j1] = NULL;
n = pface[j][0];
if(n<0)
ermes("n<0 in transform");
if((pface[j1] = (int *)farcalloc(n+1, sizeof(int))) == NULL)
ermes("Not enough memory");
pface[j1][0] = n;
for(k=1; k<=n; k++) {
k1 = (refl ? (n > 3 ? (k < 4 ? 4-k : 4+n-k) : n+1-k) :k);
m = pface[j][k1];
mabs = abs(m);
if(mabs < lowest || mabs > highest)
break;
pface[j1][k] =
(mabs == Pabs && P < 0 ||
mabs == Qabs && Q < 0 ||
mabs == Rabs && R < 0
? m : (m < 0 ? -p[mabs].inuse : p[m].inuse));
}
if(mabs < lowest || mabs > highest) {
farfree(pface[j1]);
freepos--;
}
}
nface = freepos;
}
else if(refl) { /* Invert the orientation of all faces */
for(j=0; j<nface; j++) {
if(pface[j] == NULL)
continue;
n = pface[j][0];
if(n < 3)
continue;
pnum = pface[j];
tmp = pnum[1];
pnum[1] = pnum[3];
pnum[3] = tmp;
k1 = (n - 3)/2;
for(k=1; k<=k1; k++) {
tmp = pnum[3+k];
pnum[3+k] = pnum[n+1-k];
pnum[n+1-k] = tmp;
} /* E.g. old: 1 2 3 4 5 6 7 8. */
} /* new: 3 2 1 8 7 6 5 4. */
} /* (Any vertex except 2 may become concave. */
display();
}
static void viewing(float x, float y, float z,
double *pxe, double *pye, double *pze)
{
*pxe = v11*x + v21*y;
*pye = v12*x + v22*y + v32*z;
*pze = v13*x + v23*y + v33*z + v43;
if(*pze < EPS)
ermes("Please use greater value of rho");
}
static void wrfile(void)
{
int i, j, n, k;
char ch;
FILE *fp;
grmes(LIN2, "Output file: ");
if(kbhit())
getch();
if(*filnam)
grmes(LIN3, filnam);
textcursor(margin + 8*strlen(filnam)+8, LIN3);
if(ch = mygetch(), ch != '\n' && ch != '\r') {
ungetch(ch);
getstr(margin, LIN3, filnam);
}
if(*filnam == '\0' || (fp = fopen(filnam, "w")) == NULL) {
grmes(LIN3, "Can't poen file");
return;
}
for(i=0; i<nmax; i++)
if(p[i].inuse)
fprintf(fp, "%d %f %f %f\n", i, p[i].xw, p[i].yw, p[i].zw);
if(nface > 0) {
fprintf(fp, "Faces:\n");
for(j=0; j<nface; j++) {
if(pface[j] == NULL)
continue;
n = pface[j][0];
if(n < 0)
ermes("n<0 in wrfile");
for(k=1; k<=n; k++)
fprintf(fp, " %d", pface[j][k]);
fprintf(fp, ".\n");
}
}
grmes(LIN2, " ");
grmes(LIN3, " ");
fclose(fp);
modified = 0; /* File and data structure identical */
}
static void zoom(int code)
/* zoom can be called after initgr */
{
if(code) { /* 1 = large, 0 = small */
Xvp_min = 0.25;
Xvp_max = x_max - 0.25;
Yvp_min = 0.4;
Yvp_max = y_max - 0.4;
}
else {
Xvp_min = 0.25;
Xvp_max = 6.3;
Yvp_min = 0.4;
Yvp_max = 6.7;
} /* zoom(0) will not be called when pridim = 1 */
/* so we have x_max = 10.0, y_max = 7.0 */
zoomin = code;
Xvp_range = Xvp_max - Xvp_min;
Yvp_range = Yvp_max - Yvp_min;
Xvp_center = 0.5*(Xvp_max + Xvp_min);
Yvp_center = 0.5*(Yvp_max + Yvp_min);
}